import UIKit
import Foundation

private weak var currentFirstResponder: UIView?

private var dw_view_boardInfo: UInt8 = 98

private var dw_view_boardTool: UInt8 = 97

private let dw_animalDuration: CGFloat = 0.2

enum DWKeyboardViewType : Int {
    case dw_my
    case dw_vc
    case dw_scrollView
    case dw_window
    case dw_custom
}

class DWKeyboardManage: NSObject {
    
    static let `default` : DWKeyboardManage = DWKeyboardManage.init()
    
    private weak var dw_currentView : UIView?
    
    /// 是否隐藏约束的打印
    var dw_hideConstraintLog : Bool = true {
        didSet {
            UserDefaults.standard.setValue(!dw_hideConstraintLog, forKey: "_UIConstraintBasedLayoutLogUnsatisfiable")
        }
    }
    
    var dw_enable : Bool = false
    
    var dw_tapOnResignFirstResponder : Bool = true {
        didSet {
            dw_resignFirstGest.isEnabled = dw_tapOnResignFirstResponder
        }
    }
    
    var dw_priorityClass : [AnyClass] = []
    
    var dw_showTool : Bool = true
    
    lazy var dw_toolView : DWKeyboardTool = DWKeyboardTool.init(frame: CGRect.init(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: 44))
    
    fileprivate var dw_inputViewList : [UIView] = []
    
    private lazy var dw_resignFirstGest : UITapGestureRecognizer = UITapGestureRecognizer.init(target: self, action: #selector(dw_resignFirstResponder))
        
    private override init() {
        super.init()
        NotificationCenter.default.addObserver(self, selector: #selector(dw_keyboardFrameChange(noti:)), name: UIApplication.keyboardWillChangeFrameNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(dw_textFieldDidBeginEditing(noti:)), name: Notification.Name(rawValue: UITextField.textDidBeginEditingNotification.rawValue), object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.dw_textViewDidEndEditing(noti:)), name: Notification.Name(rawValue: UITextView.textDidBeginEditingNotification.rawValue), object: nil)
        UserDefaults.standard.setValue(!dw_hideConstraintLog, forKey: "_UIConstraintBasedLayoutLogUnsatisfiable")
    }
    
    @objc func dw_nextResponder() {
        guard let _currentView = dw_currentView else {return}
        if let index = dw_inputViewList.firstIndex(of: _currentView) {
            if index < dw_inputViewList.count - 1 {
                let view = dw_inputViewList[index + 1]
                let bool = view.becomeFirstResponder()
                if bool == false {
                    _currentView.becomeFirstResponder()
                }
            }
        }
    }
    
    @objc func dw_preResponder() {
        guard let _currentView = dw_currentView else {return}
        if let index = dw_inputViewList.firstIndex(of: _currentView) {
            if index > 0 {
                let view = dw_inputViewList[index - 1]
                let bool = view.becomeFirstResponder()
                if bool == false {
                    _currentView.becomeFirstResponder()
                }
            }
        }
    }
    
    @objc func dw_textFieldDidBeginEditing(noti : Notification) {
        guard let textField = noti.object as? UITextField else {return}
        if (textField.dw_keyboardInfo.enable ?? dw_enable) == true && (textField.dw_keyboardInfo.showTool ?? dw_showTool) == true {
            textField.inputAccessoryView = textField.dw_KeyboardTool
        }else {
            textField.inputAccessoryView = nil
        }
    }
    
    @objc func dw_textViewDidEndEditing(noti : Notification) {
        guard let textView = noti.object as? UITextView else {return}
        if (textView.dw_keyboardInfo.enable ?? dw_enable) == true  && (textView.dw_keyboardInfo.showTool ?? dw_showTool == true) {
            textView.inputAccessoryView = textView.dw_KeyboardTool
        }else {
            textView.inputAccessoryView = nil
        }
    }
    
    @objc func dw_keyboardFrameChange(noti : Notification) {
        dw_inputViewList.removeAll()
        dw_currentView?.window?.removeGestureRecognizer(dw_resignFirstGest)
        guard let firstResponView = currentFirstResponder ?? UIView.dw_firstResponder() else {
            dw_hideKeyboard()
            return }
        currentFirstResponder = nil
        let keyboardRect = (noti.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
        if firstResponView.dw_keyboardInfo.needReload == true || ((firstResponView.dw_keyboardInfo.enable ?? dw_enable) == true && firstResponView.dw_keyboardInfo.signleView == nil) {
            let signViewModel : DWKeyboardSignModel = self.dw_getSignViewModel(view: firstResponView)
            firstResponView.dw_keyboardInfo.signleView = signViewModel.signView
            firstResponView.dw_keyboardInfo.viewType = signViewModel.viewType
        }
        /// 重置前一个弹框
        if let _currentView = dw_currentView {
            if _currentView.dw_keyboardInfo.signleView != firstResponView.dw_keyboardInfo.signleView {
                dw_hideKeyboard()
            }else {
                if let beginY = _currentView.dw_keyboardInfo.beginY {
                    firstResponView.dw_keyboardInfo.beginY = beginY
                }
                if let scrollContentInset = _currentView.dw_keyboardInfo.scrollContentInset {
                    firstResponView.dw_keyboardInfo.scrollContentInset = scrollContentInset
                }
            }
        }
        if keyboardRect.origin.y == UIScreen.main.bounds.size.height {
            dw_hideKeyboard()
            if let callBack = firstResponView.dw_keyboardInfo.callBack, (firstResponView.dw_keyboardInfo.enable ?? dw_enable) == true {
                callBack(false, noti, keyboardRect)
            }
            return
        }
        if (firstResponView.dw_keyboardInfo.enable ?? dw_enable) == false { return }
        if let callBack = firstResponView.dw_keyboardInfo.callBack {
            callBack(true, noti, keyboardRect)
            return
        }
        dw_currentView = firstResponView
        dw_currentView?.window?.addGestureRecognizer(dw_resignFirstGest)
        switch firstResponView.dw_keyboardInfo.viewType {
        case .dw_scrollView:
            guard let scrollView = firstResponView.dw_keyboardInfo.signleView as? UIScrollView else { return }
            if let scrollVC = scrollView.dw_firstViewController() {
                dw_inputViewList = scrollView.dw_deepResponderViews()
                /// scrollView在屏幕上的位置
                var scrollViewY : CGFloat = self.dw_convert(view: scrollView, window: scrollVC.view).origin.y
                /// 如果firstResponView在键盘的下面，要先将vc弹起
                let tempY = scrollViewY + firstResponView.dw_y + firstResponView.dw_height
                if tempY > keyboardRect.origin.y {
                    if scrollVC.view.dw_keyboardInfo.beginY == nil {
                        scrollVC.view.dw_keyboardInfo.beginY = scrollVC.view.dw_y
                        scrollVC.view.dw_keyboardInfo.viewType = .dw_my
                    }
                    let _tempY : CGFloat = min(scrollVC.view.dw_keyboardInfo.beginY ?? 0, keyboardRect.origin.y - tempY)
                    dw_keyboardShowAnimal(responView: scrollVC.view, y: _tempY)
                    scrollViewY += _tempY
                }
                if dw_currentView?.dw_keyboardInfo.scrollContentInset == nil {
                    dw_currentView?.dw_keyboardInfo.scrollContentInset = scrollView.contentInset
                }
                /// 获取respon在scrollView中的位置
                let beginY : CGFloat = self.dw_convert(view: firstResponView, window: scrollView).origin.y
                /// 键盘Y轴校正，因为只需要计算相对于scrollView的值，所以需要减去scrollView的Y轴
                let reviseKeyboardY : CGFloat = keyboardRect.origin.y - scrollViewY - firstResponView.dw_y + firstResponView.dw_height
                dw_viewAnimate {
                    scrollView.contentInset = UIEdgeInsets.init(top: scrollView.contentInset.top, left: scrollView.contentInset.left, bottom: max(scrollViewY + scrollView.dw_height - keyboardRect.origin.y, 0), right: scrollView.contentInset.right)
                    scrollView.contentOffset = CGPoint.init(x: scrollView.contentOffset.x, y: max(0, beginY - reviseKeyboardY))
                }
            }
            break
            
        case .dw_vc, .dw_window, .dw_custom:
            dw_inputViewList = dw_findAllInputView(view: firstResponView)
            if dw_currentView?.dw_keyboardInfo.beginY == nil {
                dw_currentView?.dw_keyboardInfo.beginY = dw_currentView?.dw_keyboardInfo.signleView?.dw_y ?? 0
            }
            let bottom : CGFloat = self.dw_convert(view: firstResponView, window: UIApplication.shared.dw_keyWindow).origin.y + firstResponView.dw_height - (dw_currentView?.dw_keyboardInfo.signleView?.dw_y ?? 0)
            dw_keyboardShowAnimal(responView: firstResponView, y: min(firstResponView.dw_keyboardInfo.beginY ?? 0 ,keyboardRect.origin.y - bottom))
            break
            
        default:
            break
        }
        
        switch dw_inputViewList.count {
        case 0, 1:
            firstResponView.dw_KeyboardTool?.preBtn.isHidden = true
            firstResponView.dw_KeyboardTool?.nextBtn.isHidden = true
        default:
            firstResponView.dw_KeyboardTool?.preBtn.isHidden = false
            firstResponView.dw_KeyboardTool?.nextBtn.isHidden = false
            if let index = dw_inputViewList.firstIndex(of: firstResponView) {
                if index == 0 {
                    firstResponView.dw_KeyboardTool?.preBtn.isEnabled = false
                    firstResponView.dw_KeyboardTool?.nextBtn.isEnabled = true
                }else if index == dw_inputViewList.count - 1 {
                    firstResponView.dw_KeyboardTool?.preBtn.isEnabled = true
                    firstResponView.dw_KeyboardTool?.nextBtn.isEnabled = false
                }else {
                    firstResponView.dw_KeyboardTool?.preBtn.isEnabled = true
                    firstResponView.dw_KeyboardTool?.nextBtn.isEnabled = true
                }
            }
        }
        
    }
    
    @objc func dw_resignFirstResponder() {
        (currentFirstResponder ?? UIView.dw_firstResponder())?.resignFirstResponder()
    }
    
    /// 键盘动画
    fileprivate func dw_keyboardShowAnimal(responView : UIView?, y : CGFloat, animalDur : CGFloat = dw_animalDuration) {
        guard let _responView = responView else { return }
        if animalDur > 0 {
            UIView.animate(withDuration: animalDur) {
                if _responView.dw_keyboardInfo.viewType == .dw_my {
                    _responView.dw_y = y
                }else {
                    _responView.dw_keyboardInfo.signleView?.dw_y = y
                }
            }
        }else {
            if _responView.dw_keyboardInfo.viewType == .dw_my {
                _responView.dw_y = y
            }else {
                _responView.dw_keyboardInfo.signleView?.dw_y = y
            }
        }
    }
    
    /// 动画block
    private func dw_viewAnimate(callBack: (() -> ())?) {
        UIView.animate(withDuration: dw_animalDuration) {
            callBack?()
        }
    }
    
    fileprivate func dw_hideKeyboard() {
        dw_viewAnimate {[weak self]in
            guard let strongSelf = self else {return}
            if let _currentView = strongSelf.dw_currentView {
                switch _currentView.dw_keyboardInfo.viewType {
                case .dw_scrollView:
                    guard let scrollView = _currentView.dw_keyboardInfo.signleView as? UIScrollView else { return }
                    if let scrollContentInset = _currentView.dw_keyboardInfo.scrollContentInset {
                        scrollView.contentInset = scrollContentInset
                    }
                    if let scrollVC = scrollView.dw_firstViewController(), let beginY = scrollVC.view.dw_keyboardInfo.beginY {
                        strongSelf.dw_keyboardShowAnimal(responView: scrollVC.view, y: beginY)
                    }
                    
                case .dw_vc, .dw_window:
                    strongSelf.dw_keyboardShowAnimal(responView: _currentView, y: _currentView.dw_keyboardInfo.beginY ?? 0)
                    
                case .dw_custom:
                    strongSelf.dw_keyboardShowAnimal(responView: _currentView, y: _currentView.dw_keyboardInfo.beginY ?? 0)
                    guard let scrollView = _currentView.dw_keyboardInfo.signleView as? UIScrollView else { return }
                    if let scrollContentInset = _currentView.dw_keyboardInfo.scrollContentInset {
                        scrollView.contentInset = scrollContentInset
                    }
                    
                default:
                    break
                }
            }
            strongSelf.dw_currentView?.dw_keyboardInfo.reSetInfo()
            strongSelf.dw_currentView = nil
        }
        dw_inputViewList.removeAll()
    }
    
    fileprivate func dw_convert(view : UIView, window : UIView?) -> CGRect {
        if let _window = window {
            return view.convert(view.bounds, to: _window)
        }
        if let _window = UIApplication.shared.dw_keyWindow {
            return view.convert(view.bounds, to: _window)
        }
        return .zero
    }
    
    fileprivate func dw_getSignViewModel(view : UIView) -> DWKeyboardSignModel {
        for _view in sequence(first: view, next: {$0?.superview}){
            if let responder = _view?.next{
                if responder.isKind(of: UIScrollView.self) && (responder.isKind(of: UITextView.self) == false) {
                    return DWKeyboardSignModel.init(signView: responder as? UIView, viewType: .dw_scrollView)
                }
                if responder.isKind(of: UIViewController.self){
                    return DWKeyboardSignModel.init(signView: (responder as? UIViewController)?.view, viewType: .dw_vc)
                }
                
                if responder.isKind(of: UIWindow.self){
                    return DWKeyboardSignModel.init(signView: _view, viewType: .dw_window)
                }
                
                if dw_priorityClass.contains(where: { priorityCls in
                    return responder.isKind(of: priorityCls)
                }) {
                    return DWKeyboardSignModel.init(signView: responder as? UIView, viewType: .dw_custom)
                }
                
            }
        }
        return DWKeyboardSignModel.init(signView: view, viewType: .dw_my)
    }
    
    func dw_findAllInputView(view : UIView?) -> [UIView] {
        guard let _view = view else { return [] }
        var array : [UIView] = []
        for subView in _view.superview?.subviews ?? [] {
            if subView.isKind(of: UITextView.self) || subView.isKind(of: UITextField.self) {
                array.append(subView)
            }
        }
        return array.dw_sortedArrayByPosition()
    }
    
}

extension UIView {
    
    var dw_y : CGFloat {
        get {
            return self.frame.origin.y
        }
        set {
            self.frame = CGRect.init(origin: CGPoint.init(x: self.frame.origin.x, y: newValue), size: self.frame.size)
        }
    }
    
    var dw_height : CGFloat {
        get {
            return self.frame.size.height
        }
        set {
            self.frame = CGRect.init(origin: self.frame.origin, size: CGSize.init(width: self.frame.size.width, height: newValue))
        }
    }
    
    var dw_x : CGFloat {
        get {
            return self.frame.origin.x
        }
        set {
            self.frame = CGRect.init(origin: CGPoint.init(x: newValue, y: self.frame.origin.y), size: self.frame.size)
        }
    }
    
    var dw_width : CGFloat {
        get {
            return self.frame.size.width
        }
        set {
            self.frame = CGRect.init(origin: self.frame.origin, size: CGSize.init(width: newValue, height: self.frame.size.height))
        }
    }
    
    fileprivate var dw_keyboardInfo:DWKeyboardInfo {
        get {
            guard let info = objc_getAssociatedObject(self, &dw_view_boardInfo) as? DWKeyboardInfo else {
                let _info : DWKeyboardInfo = DWKeyboardInfo.init()
                objc_setAssociatedObject(self, &dw_view_boardInfo, _info, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
                return _info
            }
            return info
        }
        set(newValue) {
            objc_setAssociatedObject(self, &dw_view_boardInfo, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    
    fileprivate static func dw_firstResponder() -> UIView? {
        currentFirstResponder = nil
        UIApplication.shared.sendAction(#selector(dw_findFirstResponder(_:)), to: nil, from: nil, for: nil)
        return currentFirstResponder
    }
    
    @objc fileprivate func dw_findFirstResponder(_ sender: UIView) {
        currentFirstResponder = self
    }
    
    func dw_firstViewController()->UIViewController?{
        for view in sequence(first: self.superview, next: {$0?.superview}){
            if let responder = view?.next{
                if responder.isKind(of: UIViewController.self){
                    return responder as? UIViewController
                }
            }
        }
        return nil
    }
    
    var dw_KeyboardTool : DWKeyboardTool? {
        set {
            objc_setAssociatedObject(self, &dw_view_boardTool, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
        get {
            var toolbar = inputAccessoryView as? DWKeyboardTool
            if toolbar == nil {
                toolbar = objc_getAssociatedObject(self, &dw_view_boardTool) as? DWKeyboardTool
            }
            if let unwrappedToolbar = toolbar {
                return unwrappedToolbar
            } else {
                let newToolbar = DWKeyboardManage.default.dw_toolView
                objc_setAssociatedObject(self, &dw_view_boardTool, newToolbar, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
                return newToolbar
            }
        }
    }
    
    fileprivate func dw_CanBecomeFirstResponder() -> Bool {
        var canBecomeFirstResponder = false
        if self.conforms(to: UITextInput.self) {
            if let textView = self as? UITextView {
                canBecomeFirstResponder = textView.isEditable
            } else if let textField = self as? UITextField {
                canBecomeFirstResponder = textField.isEnabled
            }
        }
        if canBecomeFirstResponder {
            canBecomeFirstResponder = isUserInteractionEnabled && !isHidden && alpha != 0.0
        }
        return canBecomeFirstResponder
    }
    
    fileprivate func dw_deepResponderViews() -> [UIView] {
        var textfields = [UIView]()
        for textField in subviews {
            if (textField == self), textField.dw_CanBecomeFirstResponder() {
                textfields.append(textField)
            }
            else if textField.subviews.count != 0, isUserInteractionEnabled, !isHidden, alpha != 0.0 {
                for deepView in textField.dw_deepResponderViews() {
                    textfields.append(deepView)
                }
            }
        }
        return textfields.sorted(by: { (view1: UIView, view2: UIView) -> Bool in
            let frame1 = view1.convert(view1.bounds, to: self)
            let frame2 = view2.convert(view2.bounds, to: self)
            if frame1.minY != frame2.minY {
                return frame1.minY < frame2.minY
            } else {
                return frame1.minX < frame2.minX
            }
        })
    }
    
    fileprivate func dw_responderSiblings() -> [UIView] {
        var tempTextFields = [UIView]()
        if let siblings = superview?.subviews {
            for textField in siblings {
                if textField == self, textField.dw_CanBecomeFirstResponder() {
                    tempTextFields.append(textField)
                }
            }
        }

        return tempTextFields
    }
    
    @discardableResult
    func dw_keyboard_enable(_ enable : Bool) -> UIView {
        dw_keyboardInfo.enable = enable
        return self
    }
    
    @discardableResult
    func dw_keyboard_showTool(_ showTool : Bool) -> UIView {
        dw_keyboardInfo.showTool = showTool
        return self
    }
    
    @discardableResult
    func dw_keyboard_needReload(_ needReload : Bool) -> UIView {
        dw_keyboardInfo.needReload = needReload
        return self
    }
    
    @discardableResult
    func dw_keyboard_callBack(_ callBack : ((Bool ,Notification, CGRect) -> ())?) -> UIView {
        dw_keyboardInfo.callBack = callBack
        return self
    }
}

class DWKeyboardInfo: NSObject {
    
    fileprivate var beginY : CGFloat?
    
    fileprivate weak var signleView : UIView?
            
    fileprivate var viewType : DWKeyboardViewType = .dw_vc
    
    fileprivate var scrollContentInset : UIEdgeInsets?
    
    fileprivate var enable : Bool?
    
    fileprivate var showTool : Bool?
    
    /// 是否需要重新查找，获取过一次信息之后，就不会再查找了，如果在外部改变了view的frame的话，改变了一次就要手动重置一次
    fileprivate var needReload : Bool = false
    
    /// 键盘的block
    fileprivate var callBack: ((Bool ,Notification, CGRect) -> ())?
    
    /// 重置信息
    fileprivate func reSetInfo() {
        beginY = nil
    }
    
}
